package mooc;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.time.Duration;

/**
 * @author or2
 * @Description 四种文件拷贝方式比较
 * - 一个个字节
 * - 带缓冲区
 * - 使用通道缓存
 * - 使用通道直接拷贝
 * @create 2021年09月16日 时间: 20:21
 */
public class FileCopyDemo {

    public static void main(String[] args) {
        String file = FileCopyDemo.class.getResource("").getFile();
        file = file.substring(1);
        Path path = Path.of(file, FileCopyDemo.class.getSimpleName() + ".class");
        Path pathOut = Path.of(file, "out.class");
        File in = path.toFile();
        File out = pathOut.toFile();

        new NoBufferStreamCopy().test(in, out);
        new BufferStreamCopy().test(in, out);
        new NioBufferCopy().test(in, out);
        new NioTransferCopy().test(in, out);
    }

    /**
     * 一个个字节拷贝
     */
    private static class NoBufferStreamCopy implements FileCopy {
        @Override
        public void copyFile(File source, File target) {
            try (InputStream fileIn = new FileInputStream(source);
                 OutputStream fileOut = new FileOutputStream(target)) {

                int isDone = 0;
                while (isDone != -1) {
                    isDone = fileIn.read();
                    fileOut.write(isDone);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 带缓冲区的拷贝
     */
    private static class BufferStreamCopy implements FileCopy {
        @Override
        public void copyFile(File source, File target) {
            try (InputStream fileIn = new BufferedInputStream(new FileInputStream(source));
                 OutputStream fileOut = new BufferedOutputStream(new FileOutputStream(target))) {

                byte[] buffer = new byte[1024];

                while (true) {
                    int isDone = fileIn.read(buffer);
                    if (isDone == -1)
                        break;
                    fileOut.write(buffer, 0, isDone);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用通道的拷贝
     */
    private static class NioBufferCopy implements FileCopy {
        @Override
        public void copyFile(File source, File target) {
            try (FileInputStream fileIn = new FileInputStream(source);
                 FileOutputStream fileOut = new FileOutputStream(target);
                 FileChannel inChannel = fileIn.getChannel();
                 FileChannel outChannel = fileOut.getChannel()) {

                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                while (inChannel.read(byteBuffer) != -1) {
//                    转换到 读模式
                    byteBuffer.flip();
//                    保证卸干净
                    while (byteBuffer.hasRemaining()) {
                        outChannel.write(byteBuffer);
                    }
//                    转换到 写模式
                    byteBuffer.clear();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 使用通道直接进行拷贝
     */
    private static class NioTransferCopy implements FileCopy {
        @Override
        public void copyFile(File source, File target) {
            try (FileInputStream fileIn = new FileInputStream(source);
                 FileOutputStream fileOut = new FileOutputStream(target);
                 FileChannel inChannel = fileIn.getChannel();
                 FileChannel outChannel = fileOut.getChannel()) {

                long transfer = 0;
                do {
                    transfer += inChannel.transferTo(0, inChannel.size(), outChannel);
                } while (transfer != inChannel.size());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

interface FileCopy {

    /**
     * 测试速度, 默认连续测试七次
     *
     * @param source 源文件
     * @param target 目标文件
     */
    default void test(File source, File target) {
        final int times = 7;

        long duration = 0L;
        for (int i = 0; i < times; i++) {
            long start = System.currentTimeMillis();
            copyFile(source, target);
            duration += System.currentTimeMillis() - start;
            target.delete();
        }

        System.out.println(getClass() + ": " + Duration.ofMillis(duration));
    }

    void copyFile(File source, File target);
}
